﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Engine.Objects;

namespace Engine.MarchCube
{
    public class BoundingBoxBuffers
    {
        public VertexBuffer Vertices;
        public int VertexCount;
        public IndexBuffer Indices;
        public int PrimitiveCount;
    }

    public class Chunk : Engine.Objects.BaseDrawObject
    {
        BoundingBoxBuffers bb;
        BoundingBox bbox = new BoundingBox();
        BasicEffect lineEffect;
        IndexBuffer[] m_TempIB;
        public List<BaseElement> m_AllIsO;
        int m_Size = 10;
        float m_SubSize = 2;
        public Chunk(NOWAEngine Game, string name, Vector3 position, Vector3 scal, Quaternion quat, int Size, float SubSize = 1)
            : base(Game, name, position, scal, quat)
        {

            m_Size = Size;
            m_SubSize = SubSize;
            m_AllIsO = new List<BaseElement>(0);


        }

        public BaseElement CreateElement(string Name, Vector3 pos)
        {
            BaseElement newEl = new BaseElement(m_Size,pos, Name);
            AddElement(newEl);
            return newEl;
        }

        public override unsafe void Rebild()
        {
            IBVB tmp = BildMesh();
            if (tmp.IB.Length > 0)
            {
                

                IB = new IndexBuffer(Game.GraphicsDevice, typeof(short), tmp.IB.Length, BufferUsage.None);
                IB.SetData(tmp.IB);
                VB = new VertexBuffer(Game.GraphicsDevice, typeof(VertexPositionColor), tmp.VB.Length, BufferUsage.None);
                VB.SetData(tmp.VB);
                splitIB(tmp.IB);
            }
            base.Rebild();
        }
        private void splitIB(short[] ib)
        {
            IBS = null;
            if (ib.Length>ushort.MaxValue)
            {
                
          
            List<IndexBuffer> split = new List<IndexBuffer>();
            
            int max=(int)Math.Floor((double)ib.Length/(double)UInt16.MaxValue);
            if (Math.IEEERemainder ((double)ib.Length,(double)UInt16.MaxValue)!=0)
            {
                max++;
            }
            for (int i = 0; i <max ; i++)
            {
                int copyCount = UInt16.MaxValue;
                if (i*UInt16.MaxValue+UInt16.MaxValue>ib.Length)
                {
                    copyCount = ib.Length - i * UInt16.MaxValue;
                }
                short[] tmp=new short[copyCount];
                Array.Copy(ib,i*ushort.MaxValue,tmp,0,copyCount);
                IndexBuffer tInd=new IndexBuffer(Game.GraphicsDevice, typeof(short), tmp.Length, BufferUsage.None);
                tInd.SetData(tmp);
                split.Add(tInd);
            }
            IBS= split.ToArray();
            }
        }
        private OutResult GetVal(float x, float y, float z, int i, float sh)
        {
            switch (i)
            {
                case 0:
                    return (CalkVal(x, y, z));
                case 1:
                    return (CalkVal(x + sh, y, z));
                case 2:
                    return (CalkVal(x + sh, y + sh, z));
                case 3:
                    return (CalkVal(x, y + sh, z));
                case 4:
                    return (CalkVal(x, y, z + sh));
                case 5:
                    return (CalkVal(x + sh, y, z + sh));
                case 6:
                    return (CalkVal(x + sh, y + sh, z + sh));
                case 7:
                    return (CalkVal(x, y + sh, z + sh));
            }
            return new OutResult();
        }
        private Color GetColor(float x, float y, float z, int i, float sh)
        {
            switch (i)
            {
                case 0:
                    return (CalkCol(x, y, z));
                case 1:
                    return (CalkCol(x + sh, y, z));
                case 2:
                    return (CalkCol(x + sh, y + sh, z));
                case 3:
                    return (CalkCol(x, y + sh, z));
                case 4:
                    return (CalkCol(x, y, z + sh));
                case 5:
                    return (CalkCol(x + sh, y, z + sh));
                case 6:
                    return (CalkCol(x + sh, y + sh, z + sh));
                case 7:
                    return (CalkCol(x, y + sh, z + sh));
            }
            return Color.Transparent;
        }
        private OutResult CalkVal(float x, float y, float z)
        {
            OutResult sum=new OutResult();
            bool frist = true;
            for (int i = 0; i < m_AllIsO.Count; i++)
            {
                if (frist)
                {
                    sum = m_AllIsO[i].chekPos(x, y, z);
                    frist = false;
                }
                else
                {
                    sum += m_AllIsO[i].chekPos(x, y, z);
                }
            }           
            return sum;
        }
        private Color CalkCol(float x, float y, float z)
        {
            Color sum = Color.Transparent;
            for (int i = 0; i < m_AllIsO.Count; i++)
            {
                if (sum == Color.Transparent)
                {

                  //  sum = m_AllIsO[i].Collor(x, y, z);
                }
                else
                {
                  //  Color tmp = m_AllIsO[i].Collor(x, y, z);
                  //  sum = new Color(sum.ToVector4() + tmp.ToVector4());
                }

            }

            return sum;
        }
        public unsafe IBVB BildMesh()
        {
            Element[] temp = new Element[3 * 3 * 3];
            Dictionary<string, int> temps = new Dictionary<string, int>(0);
            List<short> index = new List<short>(0);
            List<TRIANGLE> tril = new List<TRIANGLE>();
            TRIANGLE[] tri = null;
            List<VertexPositionColor> verteses = new List<VertexPositionColor>(0);
            double sum = 0;

            for (float x = bbox.Min.X; x < bbox.Max.X-1; x += m_SubSize)
            {
                for (float y = bbox.Min.Y; y < bbox.Max.Y-1; y += m_SubSize)
                {
                    for (float z = bbox.Min.Z; z < bbox.Max.Z-1; z += m_SubSize)
                    {
                       
                        sum = 0; 
                        tri = null; 
                       
                        GRIDCELL gc = MarchingCubes.GetBaseGrid(new Vector3(x, y, z), m_SubSize);
                        for (int i = 0; i < 8; i++)
                        {
                            OutResult resVal=GetVal(x, y, z, i, m_SubSize);
                            gc.val[i] = resVal.Iso;
                            sum += gc.val[i];
                            gc.col[i] = resVal.IsoColor;
                            //gc.col[i] = GetColor(x, y, z, i, m_SubSize);
                        }
                      
                        if (sum>0)
                        {
                            //m_SubSize-0.001f
                            int count = Engine.MarchCube.MarchingCubes.Polygonise(gc, 0f, ref tri);
                        }
                        if (tri != null)
                        {
                            for (int i = 0; i < tri.Length; i++)
                            {

                                int idx = 0;
                                TRIANGLE temptri = tri[i];
                                for (int poi = 0; poi < temptri.p.Length; poi++)
                                {
                                    string key = Math.Round(temptri.p[poi].X, 2) + ":" + Math.Round(temptri.p[poi].Y, 2) + ":" + Math.Round(temptri.p[poi].Z, 2);
                                    if (temps.TryGetValue(key, out idx))
                                    {
                                        index.Add((short)idx);
                                    }
                                    else
                                    {
                                        idx = temps.Count;
                                        // temps.Add(temptri.p[poi].X + ":" + temptri.p[poi].Y + ":" + temptri.p[poi].Z, idx);
                                        temps.Add(key, idx);
                                        index.Add((short)idx);
                                        verteses.Add(
                                                    new VertexPositionColor(
                                                         temptri.p[poi],
                                                         temptri.colPoint[poi]
                                                                     )
                                                    );
                                    }
                                }
                            }
                        }
                    }
                }
            }


            CheckTriangle(ref index);
            IBVB res = new IBVB { IB = index.ToArray(), VB = verteses.ToArray() };
            return res;
        }
        private void CheckTriangle(ref List<short> indexes)
        {
            List<int> fordel = new List<int>();
            Dictionary<string, int> temp = new Dictionary<string, int>();
            for (int i = 0; i < indexes.Count; i += 3)
            {
                int t = 0;
                string key=indexes[i] + ":" + indexes[i + 1] + ":" + indexes[i + 2] ;
                if (temp.TryGetValue(key, out t))
                {
                    fordel.Add(i);
                }
                else
                {
                    temp.Add(key, i);
                }
            }
            for (int i = 0; i < fordel.Count; i++)
            {
                indexes.RemoveRange(fordel[i] - (i * 3), 3);
            }
        }
        public unsafe IBVB BildMesh1()
        {
            Element[] temp = new Element[3 * 3 * 3];
            Dictionary<string, int> temps = new Dictionary<string, int>(0);
            List<short> index = new List<short>(0);
            List<TRIANGLE> tril = new List<TRIANGLE>();
            TRIANGLE[] tri = null;
            List<VertexPositionColor> verteses = new List<VertexPositionColor>(0);
            double sum = 0;

            for (float x = bbox.Min.X; x < bbox.Max.X / m_SubSize; x++)
            {
                for (float y = bbox.Min.Y; y < bbox.Max.Y / m_SubSize; y++)
                {
                    for (float z = bbox.Min.Z; z < bbox.Max.Z / m_SubSize; z++)
                    {
                        //if (m_Elements[x,y,z]!=null)
                        //{
                        //  //  temp = new Element[3 * 3 * 3];
                        //  //  GetCorner(ref temp, x, y, z);
                        //  //tril.AddRange(m_Elements[x, y, z].Bild(m_SubSize, temp, new Microsoft.Xna.Framework.Vector3(x, y, z)));
                        //}
                        // sum = CalkVal(x, y, z);
                        //  Color col=CalkCol(x, y, z);

                        GRIDCELL gc = MarchingCubes.GetBaseGrid(new Vector3(x, y, z), 1);
                        for (int i = 0; i < 8; i++)
                        {
                         //   gc.val[i] = GetVal(x * m_SubSize, y * m_SubSize, z * m_SubSize, i, 1);
                           // gc.col[i] = GetColor(x * m_SubSize, y * m_SubSize, z * m_SubSize, i, 1);
                        }
                        tri = null;
                        int count = Engine.MarchCube.MarchingCubes.Polygonise(gc, 0.6f, ref tri);
                        if (tri != null)
                        {
                            for (int i = 0; i < tri.Length; i++)
                            {

                                int idx = 0;
                                TRIANGLE temptri = tri[i];
                                for (int poi = 0; poi < temptri.p.Length; poi++)
                                {
                                    if (temps.TryGetValue(temptri.p[poi].X + ":" + temptri.p[poi].Y + ":" + temptri.p[poi].Z, out idx))
                                    {
                                        index.Add((short)idx);
                                    }
                                    else
                                    {
                                        idx = temps.Count;
                                        temps.Add(temptri.p[poi].X + ":" + temptri.p[poi].Y + ":" + temptri.p[poi].Z, idx);
                                        index.Add((short)idx);
                                        verteses.Add(
                                                    new VertexPositionColor(
                                                         temptri.p[poi] * m_SubSize,
                                                         temptri.colPoint[poi]
                                                                     )
                                                    );
                                    }
                                }
                            }
                        }
                    }
                }
            }



            IBVB res = new IBVB { IB = index.ToArray(), VB = verteses.ToArray() };
            return res;
        }

        private BoundingBoxBuffers CreateBoundingBoxBuffers(BoundingBox boundingBox, GraphicsDevice graphicsDevice)
        {
            BoundingBoxBuffers boundingBoxBuffers = new BoundingBoxBuffers();

            boundingBoxBuffers.PrimitiveCount = 24;
            boundingBoxBuffers.VertexCount = 48;

            VertexBuffer vertexBuffer = new VertexBuffer(graphicsDevice,
                typeof(VertexPositionColor), boundingBoxBuffers.VertexCount,
                BufferUsage.WriteOnly);
            List<VertexPositionColor> vertices = new List<VertexPositionColor>();

            const float ratio = 5.0f;

            Vector3 xOffset = new Vector3((boundingBox.Max.X - boundingBox.Min.X) / ratio, 0, 0);
            Vector3 yOffset = new Vector3(0, (boundingBox.Max.Y - boundingBox.Min.Y) / ratio, 0);
            Vector3 zOffset = new Vector3(0, 0, (boundingBox.Max.Z - boundingBox.Min.Z) / ratio);
            Vector3[] corners = boundingBox.GetCorners();

            // Corner 1.
            AddVertex(vertices, corners[0]);
            AddVertex(vertices, corners[0] + xOffset);
            AddVertex(vertices, corners[0]);
            AddVertex(vertices, corners[0] - yOffset);
            AddVertex(vertices, corners[0]);
            AddVertex(vertices, corners[0] - zOffset);

            // Corner 2.
            AddVertex(vertices, corners[1]);
            AddVertex(vertices, corners[1] - xOffset);
            AddVertex(vertices, corners[1]);
            AddVertex(vertices, corners[1] - yOffset);
            AddVertex(vertices, corners[1]);
            AddVertex(vertices, corners[1] - zOffset);

            // Corner 3.
            AddVertex(vertices, corners[2]);
            AddVertex(vertices, corners[2] - xOffset);
            AddVertex(vertices, corners[2]);
            AddVertex(vertices, corners[2] + yOffset);
            AddVertex(vertices, corners[2]);
            AddVertex(vertices, corners[2] - zOffset);

            // Corner 4.
            AddVertex(vertices, corners[3]);
            AddVertex(vertices, corners[3] + xOffset);
            AddVertex(vertices, corners[3]);
            AddVertex(vertices, corners[3] + yOffset);
            AddVertex(vertices, corners[3]);
            AddVertex(vertices, corners[3] - zOffset);

            // Corner 5.
            AddVertex(vertices, corners[4]);
            AddVertex(vertices, corners[4] + xOffset);
            AddVertex(vertices, corners[4]);
            AddVertex(vertices, corners[4] - yOffset);
            AddVertex(vertices, corners[4]);
            AddVertex(vertices, corners[4] + zOffset);

            // Corner 6.
            AddVertex(vertices, corners[5]);
            AddVertex(vertices, corners[5] - xOffset);
            AddVertex(vertices, corners[5]);
            AddVertex(vertices, corners[5] - yOffset);
            AddVertex(vertices, corners[5]);
            AddVertex(vertices, corners[5] + zOffset);

            // Corner 7.
            AddVertex(vertices, corners[6]);
            AddVertex(vertices, corners[6] - xOffset);
            AddVertex(vertices, corners[6]);
            AddVertex(vertices, corners[6] + yOffset);
            AddVertex(vertices, corners[6]);
            AddVertex(vertices, corners[6] + zOffset);

            // Corner 8.
            AddVertex(vertices, corners[7]);
            AddVertex(vertices, corners[7] + xOffset);
            AddVertex(vertices, corners[7]);
            AddVertex(vertices, corners[7] + yOffset);
            AddVertex(vertices, corners[7]);
            AddVertex(vertices, corners[7] + zOffset);

            vertexBuffer.SetData(vertices.ToArray());
            boundingBoxBuffers.Vertices = vertexBuffer;

            IndexBuffer indexBuffer = new IndexBuffer(graphicsDevice, IndexElementSize.SixteenBits, boundingBoxBuffers.VertexCount,
                BufferUsage.WriteOnly);
            indexBuffer.SetData(Enumerable.Range(0, boundingBoxBuffers.VertexCount).Select(i => (short)i).ToArray());
            boundingBoxBuffers.Indices = indexBuffer;

            return boundingBoxBuffers;
        }

        private static void AddVertex(List<VertexPositionColor> vertices, Vector3 position)
        {
            vertices.Add(new VertexPositionColor(position, Color.White));
        }
        private void CalkBB()
        {
            bbox = new BoundingBox(Vector3.Zero, Vector3.One);
            for (int i = 0; i < m_AllIsO.Count; i++)
            {
                bbox = BoundingBox.CreateMerged(bbox, m_AllIsO[i].BBox);
            }



            bb = CreateBoundingBoxBuffers(bbox, GraphicsDevice);
            base.NeedRebild = true;
        }
        public void AddElement(BaseElement element)
        {
            m_AllIsO.Add(element);
            CalkBB();
        }
        public override void Initialize()
        {

            base.Initialize();

            lineEffect = new BasicEffect(GraphicsDevice);
            lineEffect.LightingEnabled = false;
            lineEffect.TextureEnabled = false;
            lineEffect.VertexColorEnabled = true;
            CalkBB();
        }
        public override void Draw(GameTime gameTime)
        {

            DEFAULTCamera cam = (DEFAULTCamera)(((Engine.NOWAEngine)Game).Manager.GetComponent("CameraManager")).GetMaincamera();
            DrawBoundingBox(bb, lineEffect, GraphicsDevice, cam.ViewMatrix, cam.ProjectionMatrix);
            //WireFrame = true;            
                base.Draw(gameTime);
            
        }
        private void DrawBoundingBox(BoundingBoxBuffers buffers, BasicEffect effectLine, GraphicsDevice graphicsDevice, Matrix view, Matrix projection)
        {
            graphicsDevice.SetVertexBuffer(buffers.Vertices);
            graphicsDevice.Indices = buffers.Indices;

            effectLine.World = base.Transform;
            effectLine.View = view;
            effectLine.Projection = projection;

            foreach (EffectPass pass in effectLine.CurrentTechnique.Passes)
            {
                pass.Apply();
                graphicsDevice.DrawIndexedPrimitives(PrimitiveType.LineList, 0, 0,
                    buffers.VertexCount, 0, buffers.PrimitiveCount);
            }
        }

    }
}